package com.jzo2o.market.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.JsonUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.market.constants.RedisConstants;
import com.jzo2o.market.enums.ActivityStatusEnum;
import com.jzo2o.market.mapper.CouponMapper;
import com.jzo2o.market.model.domain.Coupon;
import com.jzo2o.market.model.dto.request.CouponOperationPageQueryReqDTO;
import com.jzo2o.market.model.dto.request.SeizeCouponReqDTO;
import com.jzo2o.market.model.dto.response.ActivityInfoResDTO;
import com.jzo2o.market.model.dto.response.CouponInfoResDTO;
import com.jzo2o.market.service.IActivityService;
import com.jzo2o.market.service.ICouponService;
import com.jzo2o.market.service.ICouponUseBackService;
import com.jzo2o.market.service.ICouponWriteOffService;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.mysql.utils.PageUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-09-16
 */
@Service
@Slf4j
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements ICouponService {

    @Resource(name = "seizeCouponScript")
    private DefaultRedisScript<String> seizeCouponScript;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private IActivityService activityService;

    @Resource
    private ICouponUseBackService couponUseBackService;

    @Resource
    private ICouponWriteOffService couponWriteOffService;


    /**
     * 根据活动ID查询优惠券领取记录
     */
    @Override
    public PageResult<CouponInfoResDTO> selectByActivityIdForCoupon(CouponOperationPageQueryReqDTO dto) {
        Page<Coupon> couponPage = PageUtils.parsePageQuery(dto, Coupon.class);
        Page<Coupon> page = lambdaQuery().eq(Coupon::getActivityId, dto.getActivityId())
                .orderByDesc(Coupon::getUseTime) //倒序排序
                .page(couponPage);
        List<Coupon> couponList = page.getRecords();
        List<CouponInfoResDTO> couponInfoResDTOList = BeanUtil.copyToList(couponList, CouponInfoResDTO.class);
        return new PageResult<>(page.getPages(), page.getTotal(), couponInfoResDTOList);
    }


    /**
     * 查询我的优惠券
     * lastId 上一页最后一张优惠券id
     */
    @Override
    public List<CouponInfoResDTO> selectMy(Integer status, Integer lastId) {
        LambdaQueryChainWrapper<Coupon> wrapper = lambdaQuery()
                .orderByDesc(Coupon::getCreateTime)
                .eq(Coupon::getUserId, UserContext.currentUserId())
                .eq(ObjectUtils.isNotNull(status), Coupon::getStatus, status)
                .last("limit 10");
        if (ObjectUtils.isNotNull(lastId)) {
            wrapper = wrapper.gt(Coupon::getCreateTime, getById(lastId).getCreateTime());
        }
        List<Coupon> couponList = wrapper.list();
        return BeanUtils.copyToList(couponList, CouponInfoResDTO.class);
    }


    /**
     * 抢券接口
     */
    @Resource(name = "seizeCouponScript")
    private DefaultRedisScript defaultRedisScript;

    @Override
    public void seizeCoupon(SeizeCouponReqDTO dto) {
        //1.判断活动是否存在
        ActivityInfoResDTO activityInfoResDTO = this.getActivityInfoResDTO(dto.getId());
        if (ObjectUtils.isNull(activityInfoResDTO)) {
            throw new BadRequestException("活动不存在");
        }
        //2.判断状态是否为进行中
        //获取当前活动真实状态，可能缓存预热不及时或定时任务更新状态延迟导致
        Integer realStatus = activityService.getRealStatus(activityInfoResDTO, activityInfoResDTO.getDistributeStartTime(), activityInfoResDTO.getDistributeEndTime());
        if (ObjectUtils.notEqual(realStatus, ActivityStatusEnum.DISTRIBUTING.getStatus())) {
            throw new BadRequestException("非法状态");
        }
        //3.判断时间是否合法
        if (LocalDateTime.now().isBefore(activityInfoResDTO.getDistributeStartTime()) || LocalDateTime.now().isAfter(activityInfoResDTO.getDistributeEndTime())) {
            throw new BadRequestException("非法时间异常");
        }
        //4. 准备Lua脚本的key和argv
        Long activityId = dto.getId();//ARGV[1] 活动id
        Long userId = UserContext.currentUser().getId(); //ARGV[2] 用户id
        List keys = Arrays.asList(
                //KEYS[1] 抢券同步队列 RedisKey
                String.format(RedisConstants.RedisKey.COUPON_SEIZE_SYNC_QUEUE_NAME, activityId % 10),
                //KEYS[2] 优惠券活动库存 RedisKey
                String.format(RedisConstants.RedisKey.COUPON_RESOURCE_STOCK, activityId % 10),
                //KEYS[3] 抢券成功队列 RedisKey
                String.format(RedisConstants.RedisKey.COUPON_SEIZE_LIST, activityId, activityId % 10)
        );
        //5.调用lua脚本执行抢券处理
        Object result = redisTemplate.execute(defaultRedisScript, keys, activityId, userId);
        if (ObjectUtils.isNull(result)) {
            throw new CommonException("抢券失败");
        }
        //6.解析结果
        Long code = Long.valueOf(result.toString());
        if (code > 0) {
            return; //抢券成功
        }
        if (code == -1) {
            throw new CommonException("限领一张");
        }

        if (code == -2 || code == -4) {
            throw new CommonException("已抢光");
        }

        if (code == -3 || code == -5) {
            throw new CommonException("抢券失败");
        }
    }


    /**
     * 获取活动对象
     */
    private ActivityInfoResDTO getActivityInfoResDTO(Long activityId) {
        //1.查询reids缓存
        Object json = redisTemplate.opsForValue().get(RedisConstants.RedisKey.ACTIVITY_CACHE_LIST);
        if (ObjectUtils.isNull(json)) {
            return null;
        }
        //2.转为list集合
        List<ActivityInfoResDTO> activityInfoResDTOList = JsonUtils.toList(json.toString(), ActivityInfoResDTO.class);
        //3.查询符合条件的对象取出
        return activityInfoResDTOList.stream().filter(x -> ObjectUtils.equals(x.getId(), activityId)).findFirst().orElse(null);

    }
}
